
#include <stdio.h>
#include <BPainter>
#include <BMouseEvent>
#include <BCursor>
#include <BApplication>
#include <BSystem>
#include <BScreen>

#include <BDialog>
#include "member_BWidget.h"
#include "member_BDialog.h"
#include "member_BEvent.h"
#include "IWindow.h"

using namespace BWE;

#define member						(*(member_BDialog*)_ptr)
#define member_allocate()			_ptr = new member_BDialog(this)
#define member_release()			delete (member_BDialog*)_ptr

BDialog::BDialog(BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setBorder(5);
	this->hide();
	this->addTitleButton(Button_Close);
}
BDialog::BDialog(const BString& title, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setTitle(title);
	this->setBorder(5);
	this->hide();
	this->addTitleButton(Button_Close);
}
BDialog::~BDialog()
{
	member_release();
}

bool BDialog::setTitle(const BString& title)
{
	if (BWidget::setTitle(title))
	{
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
bool BDialog::setTitleIcon(BIcon* titleIcon)
{
	if (BWidget::setTitleIcon(titleIcon))
	{
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
void BDialog::setFrameless(bool frameless)
{
	BWidget::setFrameless(frameless);
	member.dirty = true;
}

bool BDialog::setTitleIcon(StyleRole iconRole)
{
	BIcon* icon = BSystem::TypedIcon(iconRole);
	return setTitleIcon(icon);
}

void BDialog::setTitleAlign(Align titleAlign)
{
	if (member.alignment.ia_align != titleAlign)
	{
		member.alignment.ia_align = titleAlign;
		member.dirty = true;
		this->fresh();
	}
}
Align BDialog::titleAlign() const
{
	return member.alignment.ia_align;
}

void BDialog::setTitleButtonAlign(Align titleButtonAlign)
{
	if (member.buttonAlign)
	{
		member.buttonAlign = titleButtonAlign;
		member.dirty = true;
		this->fresh();
	}
}
Align BDialog::titleButtonAlign() const
{
	return member.buttonAlign;
}

BButton* BDialog::addTitleButton(Button button)
{
	for (int i = 0; i < member.buttonLayout->count(); i++)
	{
		BWidget* button = member.buttonLayout->widget(i);
		if (button->asset() == button)
			return (BButton*)button;
	}
	BButton* btn = new BButton(this);
	btn->setAsset(button);
	if (Graph graph = member.ButtonGraph(button))
	{
		btn->setGraph(graph);
		btn->setForm(Form_IconAlone);
	}
	if (const char* name = member.ButtonName(button))
	{
		btn->setName(name);
	}
	member.buttonLayout->addWidget(btn);
	member.connect(btn, Signal_Clicked, &member_BDialog::slotTitleButtonClicked);
	return btn;
}
bool BDialog::addTitleButton(BButton* button)
{
	if (!button)
		return false;
	if (!member.buttonLayout->contain(button))
	{
		this->addChild(button);
		member.buttonLayout->addWidget(button);
		member.connect(button, Signal_Clicked, &member_BDialog::slotTitleButtonClicked);
	}
	return true;
}

BButton* BDialog::insertTitleButton(int pos, Button button)
{
	if (pos < 0 || pos > member.buttonLayout->count())
		return 0;
	for (int i = 0; i < member.buttonLayout->count(); i++)
	{
		BWidget* button = member.buttonLayout->widget(i);
		if (button->asset() == button)
			return (BButton*)button;
	}
	BButton* btn = new BButton(this);
	btn->setAsset(button);
	if (Graph graph = member.ButtonGraph(button))
	{
		btn->setGraph(graph);
		btn->setForm(Form_IconAlone);
	}
	if (const char* name = member.ButtonName(button))
	{
		btn->setName(name);
	}
	member.buttonLayout->insertWidget(pos, btn);
	member.connect(btn, Signal_Clicked, &member_BDialog::slotTitleButtonClicked);
	return btn;
}
bool BDialog::insertTitleButton(int pos, BButton* button)
{
	if (!button)
		return false;
	if (pos < 0 || pos > member.buttonLayout->count())
		return 0;
	if (!member.buttonLayout->contain(button))
	{
		this->addChild(button);
		member.buttonLayout->insertWidget(pos, button);
		member.connect(button, Signal_Clicked, &member_BDialog::slotTitleButtonClicked);
	}
	return true;
}

bool BDialog::removeTitleButton(Button button)
{
	for (int i = 0; i < member.buttonLayout->count(); i++)
	{
		BWidget* widget = member.buttonLayout->widget(i);
		if (widget->asset() == button)
		{
			member.disconnect(widget);
			this->removeChild(widget);
			member.buttonLayout->remove(widget);
			member.dirty = true;
			this->fresh();
			return true;
		}
	}
	return false;
}
bool BDialog::removeTitleButton(BButton* button)
{
	for (int i = 0; i < member.buttonLayout->count(); i++)
	{
		BWidget* widget = member.buttonLayout->widget(i);
		if (widget == button)
		{
			member.disconnect(widget);
			this->removeChild(widget);
			member.buttonLayout->remove(widget);
			member.dirty = true;
			this->fresh();
			return true;
		}
	}
	return false;
}
void BDialog::clearTitleButtons()
{
	if (member.buttonLayout->count())
	{
		for (int i = 0; i < member.buttonLayout->count(); i++)
		{
			BWidget* widget = member.buttonLayout->widget(i);
			member.disconnect(widget);
			this->removeChild(widget);
		}
		member.buttonLayout->clear();
		member.dirty = true;
		this->fresh();
	}
}

int BDialog::titleButtonCount() const
{
	return member.buttonLayout->count();
}
const BButton* BDialog::titleButton(int index) const
{
	BWidget* widget = member.buttonLayout->widget(index);
	BButton* button = dynamic_cast<BButton*>(widget);
	return button;
}

void BDialog::setResult(int result)
{
	member.result = result;
}
int BDialog::result() const
{
	return member.result;
}

int BDialog::execute()
{
	member.result = 0;
	this->popup();
	while (!app_done && this->visible())
	{
		App_CycleBegin();
		if (this->modal() && widget_member(this)->window)
			App_Event(widget_member(this)->window);
		else
			App_Event();
		App_Update();
		App_Frame();
		App_CycleEnd();
	}
	return member.result;
}
bool BDialog::show(ShowState state)
{
	if (member.showState != state)
	{
		if (IWindow* window = widget_member(this)->window)
		{
			if (state == Show_Normal)
				window->showNormal();
			else if (state == Show_Minimized)
				window->showMinimized();
			else if (state == Show_Maximized)
				window->showMaximized();
			else if (state == Show_FullScreen)
				window->showFullScreen();
			else
				return false;
		}
		else
		{
			if (state == Show_Minimized)
			{
				member.normalRect = this->rect();
				if (BLayout* layout = this->layout())
					layout->setEnabled(false);
				if (BLayout* bottomLayout = this->layout(Part_Bottom))
					bottomLayout->setEnabled(false);
				BSize csize = this->centerSize();
				csize.height() = 0;
				setCenterSize(csize);
			}
			else if (state == Show_Maximized)
			{
				member.normalRect = this->rect();
				BRect maxRect;
				BWidget* root = this->root();
				if (root && widget_member(root)->window && widget_member(root)->window->popupWidgets.contain(this))
					maxRect = root->centerRect();
				else
					maxRect = parent()->centerRect();
				if (this->rect() != maxRect)
					this->setRect(maxRect);
			}
			else if (state == Show_Normal)
			{
				if (BLayout* layout = this->layout())
					layout->setEnabled(true);
				if (BLayout* bottomLayout = this->layout(Part_Bottom))
					bottomLayout->setEnabled(true);
				this->setRect(member.normalRect);
				member.normalRect.reset();
			}
		}
		member.showState = state;
		return true;
	}
	BWidget::show();
	return true;
}
ShowState BDialog::showState() const
{
	return member.showState;
}
void BDialog::close(int result)
{
	member.result = result;
	BEvent closeEvent(Event_Close);
	closeEvent.setValue(member.result);
	widget_member(this)->procEvent(closeEvent);
}

BSize BDialog::sizeHint() const
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BSize btnsSize = member.buttonLayout->sizeHint();
	int minWidth = member.alignment.ia_hintSize.width() + btnsSize.width();
	int minHeight = bMax(member.alignment.ia_hintSize.height(), btnsSize.height());
	BSize titleSize = BWidget::preferSize(minWidth, minHeight);
	BSize hsize = BWidget::sizeHint();
	if (hsize.width() < titleSize.width())
		hsize.width() = titleSize.width();
	if (hsize.height() < titleSize.height())
		hsize.height() = titleSize.height();
	return hsize;
}

bool BDialog::event(const BEvent& event)
{
	if (event.type() == Event_Move)
	{
		member.moved = true;
	}
	return BWidget::event(event);
}

void BDialog::attachEvent(const BEvent& event)
{
	if (BWidget* parent = event.value())
	{
		member.connect(parent, Signal_Resize, &member_BDialog::slotParentResized);
	}
	BWidget::attachEvent(event);
}
void BDialog::detachEvent(const BEvent& event)
{
	if (BWidget* parent = event.value())
	{
		member.disconnect(parent, Signal_Resize);
	}
	BWidget::detachEvent(event);
}

void BDialog::closeEvent(const BEvent& event)
{
	emit(Signal_Close, member.result);
	BWidget::closeEvent(event);
}
void BDialog::showEvent(const BEvent& event)
{
	if (this->parent() == 0 || widget_member(this)->upperLayout == 0 || this->sizePolicy().empty())
	{
		BRect upperRect;
		BWidget* root = this->root();
		if (root != this)
		{
			IWindow* window = widget_member(root)->window;
			if (window && window->popupWidgets.contain(this))
				upperRect = root->mapTo(widget_member(this)->parent, root->size());
			else
				upperRect = this->parent()->size();
		}
		else
		{
			if (widget_member(this)->window)
			{
				const BScreen* screen = BSystem::GetScreen(BCursor::GetPos());
				upperRect = screen->rect();
			}
			else if (widget_member(this)->pixmap)
			{
				upperRect = widget_member(this)->pixmap->size();
			}
		}
		if (member.dirty)
		{
			member.freshRects();
			member.dirty = false;
		}
		if (upperRect.valid())
		{
			BRect rect = this->rect();
			if (!member.moved)
			{
				BSize hsize = this->sizeHint();
				if (rect.width() < hsize.width())
					rect.setWidth(hsize.width());
				if (rect.height() < hsize.height())
					rect.setHeight(hsize.height());
				rect = upperRect.wrap(rect);
				rect = upperRect.align(Align_Center, rect.size());
			}
			if (!upperRect.contain(rect))
				rect = upperRect.align(Align_Center, rect.size());
			this->setRect(rect);
		}
	}
	if (this->parent() && widget_member(this)->upperLayout == 0)
	{
		const BPoint& abPos = this->absolutePos();
		if (abPos.y() < 0)
			this->move(0, -abPos.y());
	}
	BWidget::showEvent(event);
}
void BDialog::hideEvent(const BEvent& event)
{
	BWidget::hideEvent(event);
}

void BDialog::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	BWidget::resizeEvent(event);
}

void BDialog::styleEvent(const BEvent& event)
{
	if (const BStyle* style = this->realStyle())
	{
		if (const BStyle* titleStyle = style->sub("title"))
		{
			member.titleHeight = -1;

			const BValue& titleHeightValue = titleStyle->value(Value_Height);
			if (titleHeightValue.valid())
				member.titleHeight = titleHeightValue;

			const BValue& titleIconSizeValue = titleStyle->value(Value_Icon_Size);
			if (titleIconSizeValue.valid())
				member.titleIconSize = titleIconSizeValue;

			const BValue& alignValue = titleStyle->value(Value_Align);
			if (alignValue.valid())
				member.alignment.ia_align = alignValue;

			const BValue& spaceValue = titleStyle->value(Value_Space);
			if (spaceValue.valid())
				member.alignment.ia_spacing = spaceValue;

			const BValue& leftValue = titleStyle->value(Value_Margin_Left);
			if (leftValue.valid())
				member.titleMargin.left() = leftValue;

			const BValue& topValue = titleStyle->value(Value_Margin_Top);
			if (topValue.valid())
				member.titleMargin.top() = topValue;

			const BValue& rightValue = titleStyle->value(Value_Margin_Right);
			if (rightValue.valid())
				member.titleMargin.right() = rightValue;

			const BValue& bottomValue = titleStyle->value(Value_Margin_Bottom);
			if (bottomValue.valid())
				member.titleMargin.bottom() = bottomValue;
		}
		if (const BStyle* buttonStyle = style->sub("button"))
		{
			const BValue& spaceValue = buttonStyle->value(Value_Space);
			if (spaceValue.valid())
				member.buttonLayout->setSpacing(spaceValue);

			const BValue& leftValue = buttonStyle->value(Value_Margin_Left);
			if (leftValue.valid())
				member.buttonLayout->setMargin(Part_Left, leftValue);

			const BValue& topValue = buttonStyle->value(Value_Margin_Top);
			if (topValue.valid())
				member.buttonLayout->setMargin(Part_Top, topValue);

			const BValue& rightValue = buttonStyle->value(Value_Margin_Right);
			if (rightValue.valid())
				member.buttonLayout->setMargin(Part_Right, rightValue);

			const BValue& bottomValue = buttonStyle->value(Value_Margin_Bottom);
			if (bottomValue.valid())
				member.buttonLayout->setMargin(Part_Bottom, bottomValue);
		}
		member.dirty = true;
	}
	BWidget::styleEvent(event);
}
void BDialog::freshEvent(const BEvent& event)
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
}
void BDialog::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawPerch();
	painter.drawTitle(State_Normal);

	if (widget_member(this)->pixmap || widget_member(this)->parent || widget_member(this)->frameless)
	{
		if (this->title().size())
		{
			if (member.pressed)
				painter.setColor(Color_Title_Text_Pressed);
			else if (this->hovered())
				painter.setColor(Color_Title_Text_Hovered);
			else if (this->actived())
				painter.setColor(Color_Title_Text_Actived);
			else
				painter.setColor(Color_Title_Text);
			if (member.tr_title.size())
				painter.drawText(member.alignment.ia_textRect, member.tr_title);
			else
				painter.drawText(member.alignment.ia_textRect, this->title());
		}

		BIcon* titleIcon = this->titleIcon();
		if (titleIcon)
		{
			BImage* image = titleIcon->image(State_Normal);
			if (image)
				painter.drawImage(member.alignment.ia_imageRect, image);
		}
	}
	painter.drawMargin();
	painter.drawBorder();
}

void BDialog::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	member.hoverTitle = false;
	member.pressed = false;
	BWidget::mouseEnterEvent(mouseEvent);
}
void BDialog::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	member.hoverTitle = false;
	member.pressed = false;
	BWidget::mouseLeaveEvent(mouseEvent);
}
void BDialog::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (member.resizeale())
	{
		BPoint pos = mouseEvent.pos();
		Button button = mouseEvent.button();
		if (mouseEvent.button() == Button_Left && member.resizePart)
		{
			member.pressed = true;
			member.pressedPos = mouseEvent.globalPos();
			member.pressedRect = this->rect();
			this->fresh();
		}
	}
	BWidget::mousePressEvent(mouseEvent);
}
void BDialog::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (member.resizeale())
	{
		member.pressed = false;
		member.pressedPos.reset();
		member.pressedRect.reset();
		this->fresh();
	}
	BWidget::mouseReleaseEvent(mouseEvent);
}
void BDialog::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	if (member.resizeale())
	{
		BPoint pos = mouseEvent.pos();
		if (member.pressed && member.resizePart)
		{
			BPoint gpos = mouseEvent.globalPos();
			BPoint offset = gpos - member.pressedPos;
			BRect rect = member.pressedRect;
			if (member.resizePart & Part_Title)
			{
				if (member.showState == Show_Maximized)
				{
					float xratio = float(pos.x() - member.pressedRect.x()) / member.pressedRect.width();
					float yratio = float(pos.y() - member.pressedRect.y()) / member.pressedRect.height();
					float posx = member.normalRect.x() + member.normalRect.width() * xratio;
					float posy = member.normalRect.y() + member.normalRect.height() * yratio;
					member.pressedPos = this->globalPos() + BPoint((int)posx, (int)posy);
					member.pressedRect = member.normalRect;
					show(Show_Normal);
				}
				else
				{
					rect.move(offset);
					this->setRect(rect);
				}
			}
			else
			{
				if (member.resizePart & Part_Left)
					rect.left() = rect.left() + offset.x();
				if (member.resizePart & Part_Top)
					rect.top() = rect.top() + offset.y();
				if (member.resizePart & Part_Right)
					rect.right() = rect.right() + offset.x();
				if (member.resizePart & Part_Bottom)
					rect.bottom() = rect.bottom() + offset.y();
				if (this->setSize(rect.size()))
					this->setPos(rect.pos());
			}
			return;
		}

		CursorType cursor = Cursor_None;
		bool hoverTitle = member.titleRect.contain(pos);
		if (member.hoverTitle != hoverTitle)
		{
			member.hoverTitle = hoverTitle;
			this->fresh();
		}
		member.resizePart = Part_None;
		if (!(member.ignorePart & Part_Title) && member.hoverTitle)
		{
			member.resizePart = Part_Title;
		}
		else if (!(member.ignorePart & Part_Left) && !(member.ignorePart & Part_Top) && member.cornerLeftTop.contain(pos))
		{
			member.resizePart = Part_LeftTop;
			cursor = Cursor_SizeLeftTop;
		}
		else if (!(member.ignorePart & Part_Left) && !(member.ignorePart & Part_Bottom) && member.cornerLeftBottom.contain(pos))
		{
			member.resizePart = Part_LeftBottom;
			cursor = Cursor_SizeLeftBottom;
		}
		else if (!(member.ignorePart & Part_Right) && !(member.ignorePart & Part_Top) && member.cornerRightTop.contain(pos))
		{
			member.resizePart = Part_RightTop;
			cursor = Cursor_SizeRightTop;
		}
		else if (!(member.ignorePart & Part_Right) && !(member.ignorePart & Part_Bottom) && member.cornerRightBottom.contain(pos))
		{
			member.resizePart = Part_RightBottom;
			cursor = Cursor_SizeRightBottom;
		}
		else if (!(member.ignorePart & Part_Left) && member.borderLeft.contain(pos))
		{
			member.resizePart = Part_Left;
			cursor = Cursor_SizeHor;
		}
		else if (!(member.ignorePart & Part_Right) && member.borderRight.contain(pos))
		{
			member.resizePart = Part_Right;
			cursor = Cursor_SizeHor;
		}
		else if (!(member.ignorePart & Part_Top) && member.borderTop.contain(pos))
		{
			member.resizePart = Part_Top;
			cursor = Cursor_SizeVer;
		}
		else if (!(member.ignorePart & Part_Bottom) && member.borderBottom.contain(pos))
		{
			member.resizePart = Part_Bottom;
			cursor = Cursor_SizeVer;
		}
		if (!this->cursor() || this->cursor()->type() != cursor)
		{
			this->setCursor(cursor);
		}
	}
	BWidget::mouseMoveEvent(mouseEvent);
}
bool BDialog::mouseWheelEvent(const BMouseEvent& mouseEvent)
{
	return true;
}
void BDialog::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	if (member.resizeale() && member.resizePart == Part_Title)
	{
		BPoint pos = mouseEvent.pos();
		Button button = mouseEvent.button();
		if (button == Button_Left && member.titleRect.contain(pos))
		{
			if (member.showState == Show_Maximized)
				show(Show_Normal);
			else
				show(Show_Maximized);
		}
	}
	BWidget::mouseDoubleClickEvent(mouseEvent);
}
